msg_tool\scripts\hexen_haus/
bin.rs

1//! HexenHaus Script File (.bin)
2use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::*;
6use crate::utils::str::*;
7use anyhow::Result;
8use std::io::Read;
9
10#[derive(Debug)]
11/// HexenHaus Script Builder
12pub struct BinScriptBuilder {}
13
14impl BinScriptBuilder {
15    /// Creates a new instance of `BinScriptBuilder`
16    pub fn new() -> Self {
17        BinScriptBuilder {}
18    }
19}
20
21impl ScriptBuilder for BinScriptBuilder {
22    fn default_encoding(&self) -> Encoding {
23        Encoding::Cp932
24    }
25
26    fn build_script(
27        &self,
28        buf: Vec<u8>,
29        _filename: &str,
30        encoding: Encoding,
31        _archive_encoding: Encoding,
32        config: &ExtraConfig,
33        _archive: Option<&Box<dyn Script>>,
34    ) -> Result<Box<dyn Script>> {
35        Ok(Box::new(BinScript::new(buf, encoding, config)?))
36    }
37
38    fn extensions(&self) -> &'static [&'static str] {
39        &["bin"]
40    }
41
42    fn script_type(&self) -> &'static ScriptType {
43        &ScriptType::HexenHaus
44    }
45
46    fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
47        if buf_len >= 4 && buf.starts_with(b"NORI") {
48            return Some(10);
49        }
50        None
51    }
52}
53
54#[derive(Debug)]
55struct BinString {
56    str: String,
57    pos: usize,
58    len: usize,
59}
60
61#[derive(Debug)]
62/// HexenHaus Bin Script
63pub struct BinScript {
64    data: MemReader,
65    strs: Vec<BinString>,
66}
67
68impl BinScript {
69    /// Creates a new `BinScript`
70    ///
71    /// * `buf` - The buffer containing the bin script data
72    /// * `encoding` - The encoding of the script
73    /// * `config` - Extra configuration options
74    pub fn new(buf: Vec<u8>, encoding: Encoding, _config: &ExtraConfig) -> Result<Self> {
75        let mut data = MemReader::new(buf);
76        let mut header = [0; 4];
77        data.read_exact(&mut header)?;
78        if header != *b"NORI" {
79            return Err(anyhow::anyhow!("Invalid HexenHaus bin script header"));
80        }
81        for c in data.data.iter_mut() {
82            *c ^= 0x53;
83        }
84        data.pos = memchr::memmem::find(&data.data, b"_beginrp")
85            .ok_or(anyhow::anyhow!("Failed to find _beginrp"))?;
86        data.pos += 16;
87        let mut p = [0; 2];
88        let mut s = Vec::new();
89        let data_len = data.data.len();
90        let mut start_pos = data.pos;
91        let mut strs = Vec::new();
92        while data.pos < data_len {
93            data.read_exact(&mut p)?;
94            if p[0] == 0x53 {
95                if s.len() > 2 {
96                    if let Ok(c) = decode_to_string(encoding, &s[s.len() - 2..], true) {
97                        if c != "」" && c != "。" && c != "』" {
98                            s.pop();
99                            s.pop();
100                        }
101                    } else {
102                        s.pop();
103                        s.pop();
104                    }
105                }
106                if s.len() > 2 {
107                    let d = decode_to_string(encoding, &s, true)?;
108                    strs.push(BinString {
109                        str: d,
110                        pos: start_pos,
111                        len: s.len(),
112                    });
113                }
114                start_pos = data.pos;
115                s.clear();
116            } else if p[1] == 0x53 {
117                if s.len() > 2 {
118                    let d = decode_to_string(encoding, &s, true)?;
119                    strs.push(BinString {
120                        str: d,
121                        pos: start_pos,
122                        len: s.len(),
123                    });
124                }
125                start_pos = data.pos;
126                s.clear();
127            } else {
128                s.extend_from_slice(&p);
129            }
130        }
131        if s.len() > 2 {
132            s.pop();
133            s.pop();
134            if s.len() > 2 {
135                let d = decode_to_string(encoding, &s, true)?;
136                strs.push(BinString {
137                    str: d,
138                    pos: start_pos,
139                    len: s.len(),
140                });
141            }
142        }
143        Ok(BinScript { data, strs })
144    }
145}
146
147impl Script for BinScript {
148    fn default_output_script_type(&self) -> OutputScriptType {
149        OutputScriptType::Json
150    }
151
152    fn default_format_type(&self) -> FormatOptions {
153        FormatOptions::None
154    }
155
156    fn extract_messages(&self) -> Result<Vec<Message>> {
157        let mut messages: Vec<Message> = Vec::new();
158        for str in &self.strs {
159            let message = if let Some(ind) = str.str.find("「") {
160                let (name, mes) = str.str.split_at(ind);
161                let mut name = name.to_string();
162                if name.is_empty() {
163                    if let Some(m) = messages.pop() {
164                        name = m.message;
165                    }
166                }
167                Message {
168                    name: Some(name.to_string()),
169                    message: mes.to_string(),
170                }
171            } else {
172                Message {
173                    name: None,
174                    message: str.str.clone(),
175                }
176            };
177            messages.push(message);
178        }
179        Ok(messages)
180    }
181
182    fn import_messages<'a>(
183        &'a self,
184        mut messages: Vec<Message>,
185        mut file: Box<dyn WriteSeek + 'a>,
186        _filename: &str,
187        encoding: Encoding,
188        replacement: Option<&'a ReplacementTable>,
189    ) -> Result<()> {
190        let mut data = MemWriter::from_vec(self.data.data.clone());
191        let mut i = 0;
192        for str in self.strs.iter() {
193            if i >= messages.len() {
194                return Err(anyhow::anyhow!("Not enough messages."));
195            }
196            if let Some(ind) = str.str.find("「") {
197                let (name, _) = str.str.split_at(ind);
198                let mut target = String::new();
199                if !name.is_empty() {
200                    let mut name = match &messages[i].name {
201                        Some(n) => n.to_owned(),
202                        None => return Err(anyhow::anyhow!("Missing name for message.")),
203                    };
204                    if let Some(repl) = replacement {
205                        for (k, v) in &repl.map {
206                            name = name.replace(k, v);
207                        }
208                    };
209                    target.push_str(&name);
210                }
211                let mut mes = messages[i].message.clone();
212                if let Some(repl) = replacement {
213                    for (k, v) in &repl.map {
214                        mes = mes.replace(k, v);
215                    }
216                }
217                target.push_str(&mes);
218                let mut encoded = encode_string(encoding, &target, false)?;
219                if encoded.len() > str.len {
220                    eprintln!("Warning: Message '{}' is too long, truncating.", target);
221                    crate::COUNTER.inc_warning();
222                    encoded = truncate_string(&target, str.len, encoding, false)?;
223                }
224                while encoded.len() < str.len {
225                    encoded.push(32); // Fill with spaces
226                }
227                data.write_all_at(str.pos as u64, &encoded)?;
228                i += 1;
229            } else {
230                let mut target = if let Some(name) = messages[i].name.take() {
231                    name
232                } else {
233                    let s = messages[i].message.clone();
234                    i += 1;
235                    s
236                };
237                if let Some(repl) = replacement {
238                    for (k, v) in &repl.map {
239                        target = target.replace(k, v);
240                    }
241                }
242                let mut encoded = encode_string(encoding, &target, false)?;
243                if encoded.len() > str.len {
244                    eprintln!("Warning: Message '{}' is too long, truncating.", target);
245                    crate::COUNTER.inc_warning();
246                    encoded = truncate_string(&target, str.len, encoding, false)?;
247                }
248                while encoded.len() < str.len {
249                    encoded.push(32); // Fill with spaces
250                }
251                data.write_all_at(str.pos as u64, &encoded)?;
252            }
253        }
254        let mut data = data.into_inner();
255        for d in data.iter_mut() {
256            *d ^= 0x53;
257        }
258        file.write_all(&data)?;
259        Ok(())
260    }
261}